/*
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package parser

import (
	"fmt"
	"strings"
)

// SQLNode defines the interface for all nodes
// generated by the parser.
type SQLNode interface {
	Format(buf *nodeBuffer)
}

// nodeBuffer extends strings.Builder to format SQLNode easily.
type nodeBuffer struct {
	strings.Builder
}

// Printf mimics fmt.Fprintf(buf, ...), but limited to %s for string and %v for Node.
func (buf *nodeBuffer) Printf(format string, values ...interface{}) {
	end := len(format)
	fieldnum := 0
	for i := 0; i < end; {
		lasti := i
		for i < end && format[i] != '%' {
			i++
		}
		if i > lasti {
			buf.WriteString(format[lasti:i])
		}
		if i >= end {
			break
		}
		i++ // '%'
		switch format[i] {
		case 's':
			switch v := values[fieldnum].(type) {
			case []byte:
				buf.Write(v)
			case string:
				buf.WriteString(v)
			default:
				panic(fmt.Sprintf("unexpected string type %T", v))
			}
		case 'v':
			node := values[fieldnum].(SQLNode)
			node.Format(buf)
		default:
			panic(fmt.Sprintf("unexpected format: %c", format[i]))
		}
		fieldnum++
		i++
	}
}

// String returns a string representation of an SQLNode.
func String(node SQLNode) string {
	if node == nil {
		return "<nil>"
	}

	var buf nodeBuffer
	node.Format(&buf)
	return buf.String()
}

// Statement represents a statement.
type Statement interface {
	iStatement()
	SQLNode
}

func (*Union) iStatement()         {}
func (*Select) iStatement()        {}
func (*Stream) iStatement()        {}
func (*Insert) iStatement()        {}
func (*Update) iStatement()        {}
func (*Delete) iStatement()        {}
func (*Set) iStatement()           {}
func (*Declare) iStatement()       {}
func (*Cursor) iStatement()        {}
func (*BeginEnd) iStatement()      {}
func (*While) iStatement()         {}
func (*If) iStatement()            {}
func (*DDL) iStatement()           {}
func (*Show) iStatement()          {}
func (*Use) iStatement()           {}
func (*Begin) iStatement()         {}
func (*Commit) iStatement()        {}
func (*Rollback) iStatement()      {}
func (*OtherRead) iStatement()     {}
func (*OtherAdmin) iStatement()    {}
func (*SetBoolOption) iStatement() {}

// ParenSelect can actually not be a top level statement,
// but we have to allow it because it's a requirement
// of SelectStatement.
func (*ParenSelect) iStatement() {}

// SelectStatement any SELECT statement.
type SelectStatement interface {
	iSelectStatement()
	iStatement()
	iInsertRows()
	addOrder(*Order)
	setLimit(*Limit)
	SQLNode
}

func (*Select) iSelectStatement()      {}
func (*Union) iSelectStatement()       {}
func (*ParenSelect) iSelectStatement() {}

// Select represents a SELECT statement.
type Select struct {
	Cache       string
	Comments    Comments
	Distinct    string
	Hints       string
	SelectExprs SelectExprs
	From        TableExprs
	Where       *Where
	GroupBy     GroupBy
	Having      *Where
	OrderBy     OrderBy
	Limit       *Limit
	Lock        string
}

// Select.Distinct
const (
	DistinctStr      = "distinct "
	StraightJoinHint = "straight_join "
)

// Select.Lock
const (
	ForUpdateStr = " for update"
	ShareModeStr = " lock in share mode"
)

// Select.Cache
const (
	SQLCacheStr   = "sql_cache "
	SQLNoCacheStr = "sql_no_cache "
)

// addOrder adds an order by element
func (node *Select) addOrder(order *Order) {
	node.OrderBy = append(node.OrderBy, order)
}

// setLimit sets the limit clause
func (node *Select) setLimit(limit *Limit) {
	node.Limit = limit
}

// Format formats the node.
func (node *Select) Format(buf *nodeBuffer) {
	buf.Printf("select %v%s%s%s%v from %v%v%v%v%v%v%s",
		node.Comments, node.Cache, node.Distinct, node.Hints, node.SelectExprs,
		node.From, node.Where,
		node.GroupBy, node.Having, node.OrderBy,
		node.Limit, node.Lock)
}

// addWhere adds the boolean expression to the
// WHERE clause as an AND condition. If the expression
// is an OR clause, it parenthesizes it. Currently,
// the OR operator is the only one that's lower precedence
// than AND.
func (node *Select) addWhere(expr Expr) {
	if _, ok := expr.(*OrExpr); ok {
		expr = &ParenExpr{Expr: expr}
	}
	if node.Where == nil {
		node.Where = &Where{
			Type: WhereStr,
			Expr: expr,
		}
		return
	}
	node.Where.Expr = &AndExpr{
		Left:  node.Where.Expr,
		Right: expr,
	}
	return
}

// addHaving adds the boolean expression to the
// HAVING clause as an AND condition. If the expression
// is an OR clause, it parenthesizes it. Currently,
// the OR operator is the only one that's lower precedence
// than AND.
func (node *Select) addHaving(expr Expr) {
	if _, ok := expr.(*OrExpr); ok {
		expr = &ParenExpr{Expr: expr}
	}
	if node.Having == nil {
		node.Having = &Where{
			Type: HavingStr,
			Expr: expr,
		}
		return
	}
	node.Having.Expr = &AndExpr{
		Left:  node.Having.Expr,
		Right: expr,
	}
	return
}

// ParenSelect is a parenthesized SELECT statement.
type ParenSelect struct {
	Select SelectStatement
}

// addOrder adds an order by element
func (node *ParenSelect) addOrder(order *Order) {
	panic("unreachable")
}

// setLimit sets the limit clause
func (node *ParenSelect) setLimit(limit *Limit) {
	panic("unreachable")
}

// Format formats the node.
func (node *ParenSelect) Format(buf *nodeBuffer) {
	buf.Printf("(%v)", node.Select)
}

// Union represents a UNION statement.
type Union struct {
	Type        string
	Left, Right SelectStatement
	OrderBy     OrderBy
	Limit       *Limit
	Lock        string
}

// Union.Type
const (
	UnionStr         = "union"
	UnionAllStr      = "union all"
	UnionDistinctStr = "union distinct"
)

// addOrder adds an order by element
func (node *Union) addOrder(order *Order) {
	node.OrderBy = append(node.OrderBy, order)
}

// setLimit sets the limit clause
func (node *Union) setLimit(limit *Limit) {
	node.Limit = limit
}

// Format formats the node.
func (node *Union) Format(buf *nodeBuffer) {
	buf.Printf("%v %s %v%v%v%s", node.Left, node.Type, node.Right,
		node.OrderBy, node.Limit, node.Lock)
}

// Stream represents a SELECT statement.
type Stream struct {
	Comments   Comments
	SelectExpr SelectExpr
	Table      TableName
}

// Format formats the node.
func (node *Stream) Format(buf *nodeBuffer) {
	buf.Printf("stream %v%v from %v",
		node.Comments, node.SelectExpr, node.Table)
}

// Insert represents an INSERT or REPLACE statement.
// Per the MySQL docs, http://dev.mysql.com/doc/refman/5.7/en/replace.html
// Replace is the counterpart to `INSERT IGNORE`, and works exactly like a
// normal INSERT except if the row exists. In that case it first deletes
// the row and re-inserts with new values. For that reason we keep it as an Insert struct.
// Replaces are currently disallowed in sharded schemas because
// of the implications the deletion part may have on the original parser.
// If you add fields here, consider adding them to calls to validateSubquerySamePlan.
type Insert struct {
	Action     string
	Comments   Comments
	Ignore     string
	Table      TableName
	Partitions Partitions
	Columns    Columns
	Rows       InsertRows
	OnDup      OnDup
}

// DDL strings.
const (
	InsertStr  = "insert"
	ReplaceStr = "replace"
)

// Format formats the node.
func (node *Insert) Format(buf *nodeBuffer) {
	buf.Printf("%s %v%sinto %v%v%v %v%v",
		node.Action,
		node.Comments, node.Ignore,
		node.Table, node.Partitions, node.Columns, node.Rows, node.OnDup)
}

// InsertRows represents the rows for an INSERT statement.
type InsertRows interface {
	iInsertRows()
	SQLNode
}

func (*Select) iInsertRows()      {}
func (*Union) iInsertRows()       {}
func (Values) iInsertRows()       {}
func (*ParenSelect) iInsertRows() {}

// Update represents an UPDATE statement.
// If you add fields here, consider adding them to calls to validateSubquerySamePlan.
type Update struct {
	Comments   Comments
	TableExprs TableExprs
	Exprs      UpdateExprs
	From       TableExprs
	Where      *Where
	OrderBy    OrderBy
	Limit      *Limit
}

// Format formats the node.
func (node *Update) Format(buf *nodeBuffer) {
	buf.Printf("update %v%v set %v", node.Comments, node.TableExprs, node.Exprs)
	if node.From != nil {
		buf.Printf(" from %v", node.From);
	}
	buf.Printf("%v%v%v", node.Where, node.OrderBy, node.Limit)
}

// Delete represents a DELETE statement.
// If you add fields here, consider adding them to calls to validateSubquerySamePlan.
type Delete struct {
	Comments   Comments
	Targets    TableNames
	TableExprs TableExprs
	Partitions Partitions
	Where      *Where
	OrderBy    OrderBy
	Limit      *Limit
}

// Format formats the node.
func (node *Delete) Format(buf *nodeBuffer) {
	buf.Printf("delete %v", node.Comments)
	if node.Targets != nil {
		buf.Printf("%v ", node.Targets)
	}
	buf.Printf("from %v%v%v%v%v", node.TableExprs, node.Partitions, node.Where, node.OrderBy, node.Limit)
}

// Set represents a SET statement.
type Set struct {
	Comments Comments
	Exprs    SetExprs
	Scope    string
}

// Set.Scope or Show.Scope
const (
	SessionStr = "session"
	GlobalStr  = "global"
)

// Format formats the node.
func (node *Set) Format(buf *nodeBuffer) {
	if node.Scope == "" {
		buf.Printf("set %v%v", node.Comments, node.Exprs)
	} else {
		buf.Printf("set %v%s %v", node.Comments, node.Scope, node.Exprs)
	}
}

// DDL represents a CREATE, ALTER, DROP, RENAME or TRUNCATE statement.
// Table is set for AlterStr, DropStr, RenameStr, TruncateTable
// NewName is set for AlterStr, CreateStr, RenameStr.
type DDL struct {
	Action        DDLAction
	Table         TableName
	NewName       TableName
	IfExists      bool
	TableSpec     *TableSpec
	PartitionSpec *PartitionSpec
	IndexSpec     *IndexSpec
	IndexCols     []IndexColumn
	IndexExpr     Expr
	ForeignKey    *ForeignKeyDefinition
	Exclusion     *ExclusionDefinition
	Policy        *Policy
	View          *View
	Trigger       *Trigger
	Type          *Type
	Comment       *Comment
	Extension     *Extension
	Schema        *Schema
}

type DDLAction int

// DDL actions
const (
	AddForeignKey = DDLAction(iota)
	AddIndex
	AddPrimaryKey
	AddExclusion
	CommentOn
	CreateExtension
	CreateIndex
	CreatePolicy
	CreateTable
	CreateTrigger
	CreateType
	CreateView
	CreateSchema
)

// View types
const (
	ViewStr             = "view"
	MaterializedViewStr = "materialized view"
	SqlSecurityStr      = "sql security"
)

// Format formats the node.
func (node *DDL) Format(buf *nodeBuffer) {
	switch node.Action {
	case CreateTable:
		if node.TableSpec == nil {
			buf.Printf("create table %v", node.NewName)
		} else {
			buf.Printf("create table %v %v", node.NewName, node.TableSpec)
		}
	case CreateView:
		if node.View.SecurityType != "" {
			buf.Printf("alter %v view %v as %v", node.View.SecurityType, node.View.Name, node.View.Definition)
		} else {
			buf.Printf("alter %v as %v", node.View.Name, node.View.Definition)
		}
	default:
		panic(fmt.Sprintf("unexpected action: %v", node.Action))
	}
}

// Partition strings
const (
	ReorganizeStr = "reorganize partition"
)

// PartitionSpec describe partition actions (for alter and create)
type PartitionSpec struct {
	Action      string
	Name        ColIdent
	Definitions []*PartitionDefinition
}

// Format formats the node.
func (node *PartitionSpec) Format(buf *nodeBuffer) {
	switch node.Action {
	case ReorganizeStr:
		buf.Printf("%s %v into (", node.Action, node.Name)
		var prefix string
		for _, pd := range node.Definitions {
			buf.Printf("%s%v", prefix, pd)
			prefix = ", "
		}
		buf.Printf(")")
	default:
		panic("unimplemented")
	}
}

// PartitionDefinition describes a very minimal partition definition
type PartitionDefinition struct {
	Name     ColIdent
	Limit    Expr
	Maxvalue bool
}

// Format formats the node
func (node *PartitionDefinition) Format(buf *nodeBuffer) {
	if !node.Maxvalue {
		buf.Printf("partition %v values less than (%v)", node.Name, node.Limit)
	} else {
		buf.Printf("partition %v values less than (maxvalue)", node.Name)
	}
}

// TableSpec describes the structure of a table from a CREATE TABLE statement
type TableSpec struct {
	Columns     []*ColumnDefinition
	Indexes     []*IndexDefinition
	ForeignKeys []*ForeignKeyDefinition
	Checks      []*CheckDefinition
	Exclusions  []*ExclusionDefinition // for Postgres
	Options     map[string]string
}

// Format formats the node.
func (ts *TableSpec) Format(buf *nodeBuffer) {
	buf.Printf("(\n")
	for i, col := range ts.Columns {
		if i == 0 {
			buf.Printf("\t%v", col)
		} else {
			buf.Printf(",\n\t%v", col)
		}
	}
	for _, idx := range ts.Indexes {
		buf.Printf(",\n\t%v", idx)
	}

	options := ""
	for key, value := range ts.Options {
		options += " " + key + "=" + value
	}
	buf.Printf("\n)%s", strings.Replace(options, ", ", ",\n  ", -1))
}

// addColumn appends the given column to the list in the spec
func (ts *TableSpec) addColumn(cd *ColumnDefinition) {
	ts.Columns = append(ts.Columns, cd)
}

// addIndex appends the given index to the list in the spec
func (ts *TableSpec) addIndex(id *IndexDefinition) {
	ts.Indexes = append(ts.Indexes, id)
}

func (ts *TableSpec) addCheck(check *CheckDefinition) {
	ts.Checks = append(ts.Checks, check)
}

func (ts *TableSpec) addForeignKey(foreignKey *ForeignKeyDefinition) {
	ts.ForeignKeys = append(ts.ForeignKeys, foreignKey)
}

// ColumnDefinition describes a column in a CREATE TABLE statement
type ColumnDefinition struct {
	Name ColIdent
	Type ColumnType
}

// Format formats the node.
func (col *ColumnDefinition) Format(buf *nodeBuffer) {
	buf.Printf("%v %v", col.Name, &col.Type)
}

type Sequence struct {
	Name        string
	IfNotExists bool
	Type        string

	IncrementBy *SQLVal
	MinValue    *SQLVal
	NoMinValue  *BoolVal
	MaxValue    *SQLVal
	NoMaxValue  *BoolVal
	StartWith   *SQLVal
	Cache       *SQLVal
	Cycle       *BoolVal
	NoCycle     *BoolVal
	OwnedBy     string
}

type GeneratedColumn struct {
	Expr          Expr
	GeneratedType string
}

type IdentityOpt struct {
	Behavior          string
	Sequence          *Sequence
	NotForReplication bool
}

// ColumnType represents a sql type in a CREATE TABLE statement
// All optional fields are nil if not specified
type ColumnType struct {
	// The base type string
	Type string

	// Generic field options.
	NotNull       *BoolVal
	Autoincrement BoolVal
	Default       *DefaultDefinition
	Srid          *SridDefinition
	OnUpdate      *SQLVal
	Comment       *SQLVal
	Check         *CheckDefinition
	Array         BoolVal

	// Numeric field options
	Length       *SQLVal
	Unsigned     BoolVal
	Zerofill     BoolVal
	Scale        *SQLVal
	DisplayWidth *SQLVal

	// Text field options
	Charset string
	Collate string

	// Timestamp field options
	Timezone BoolVal

	// Enum values
	EnumValues []string

	// Key specification
	KeyOpt ColumnKeyOption

	References        string
	ReferenceNames    Columns
	ReferenceOnDelete ColIdent
	ReferenceOnUpdate ColIdent

	// MySQL: GENERATED ALWAYS AS (expr)
	Generated *GeneratedColumn

	// PostgreSQL: GENERATED AS IDENTITY
	Identity *IdentityOpt
}

type DefaultDefinition struct {
	ValueOrExpression DefaultValueOrExpression
	ConstraintName    ColIdent // only for MSSQL
}

type DefaultValueOrExpression struct {
	Value *SQLVal
	Expr  Expr
}

type SridDefinition struct {
	Value *SQLVal
}

type CheckDefinition struct {
	Where             Where
	ConstraintName    ColIdent
	NotForReplication bool
	NoInherit         BoolVal
}

type ExclusionPair struct {
	Column   ColIdent
	Operator string
}

type ExclusionDefinition struct {
	ConstraintName ColIdent
	IndexType      string
	Exclusions     []ExclusionPair
	Where          *Where
}

// Format returns a canonical string representation of the type and all relevant options
func (ct *ColumnType) Format(buf *nodeBuffer) {
	buf.Printf("%s", ct.Type)

	if ct.Length != nil && ct.Scale != nil {
		buf.Printf("(%v,%v)", ct.Length, ct.Scale)

	} else if ct.Length != nil {
		buf.Printf("(%v)", ct.Length)
	}

	if ct.EnumValues != nil {
		buf.Printf("(%s)", strings.Join(ct.EnumValues, ", "))
	}

	if ct.Unsigned {
		buf.Printf(" %s", keywordStrings[UNSIGNED])
	}
	if ct.Zerofill {
		buf.Printf(" %s", keywordStrings[ZEROFILL])
	}
	if ct.Charset != "" {
		buf.Printf(" %s %s %s", keywordStrings[CHARACTER], keywordStrings[SET], ct.Charset)
	}
	if ct.Collate != "" {
		buf.Printf(" %s %s", keywordStrings[COLLATE], ct.Collate)
	}
	if ct.Timezone {
		buf.Printf(" %s %s %s", keywordStrings[WITH], keywordStrings[TIME], keywordStrings[ZONE])
	}
	if ct.NotNull != nil && *ct.NotNull {
		buf.Printf(" %s %s", keywordStrings[NOT], keywordStrings[NULL])
	}
	if ct.Default != nil {
		buf.Printf(" %s", keywordStrings[DEFAULT])
		if ct.Default.ValueOrExpression.Value != nil {
			buf.Printf(" %s", String(ct.Default.ValueOrExpression.Value))
		} else {
			buf.Printf("(%v)", ct.Default.ValueOrExpression.Expr)
		}
	}
	if ct.OnUpdate != nil {
		buf.Printf(" %s %s %s", keywordStrings[ON], keywordStrings[UPDATE], String(ct.OnUpdate))
	}
	if ct.Autoincrement {
		buf.Printf(" %s", keywordStrings[AUTO_INCREMENT])
	}
	if ct.Comment != nil {
		buf.Printf(" %s %s", keywordStrings[COMMENT_KEYWORD], String(ct.Comment))
	}
	if ct.Check != nil {
		buf.Printf(" %s %s", keywordStrings[CHECK], String(&ct.Check.Where))
	}
	if ct.KeyOpt == colKeyPrimary {
		buf.Printf(" %s %s", keywordStrings[PRIMARY], keywordStrings[KEY])
	}
	if ct.KeyOpt == colKeyUnique {
		buf.Printf(" %s", keywordStrings[UNIQUE])
	}
	if ct.KeyOpt == colKeyUniqueKey {
		buf.Printf(" %s %s", keywordStrings[UNIQUE], keywordStrings[KEY])
	}
	if ct.KeyOpt == colKeySpatialKey {
		buf.Printf(" %s %s", keywordStrings[SPATIAL], keywordStrings[KEY])
	}
	if ct.KeyOpt == colKey {
		buf.Printf(" %s", keywordStrings[KEY])
	}
}

// IndexDefinition describes an index in a CREATE TABLE statement
type IndexDefinition struct {
	Info              *IndexInfo
	Columns           []IndexColumn
	Options           []*IndexOption
	Partition         *IndexPartition
	ConstraintOptions *ConstraintOptions
}

// Format formats the node.
func (idx *IndexDefinition) Format(buf *nodeBuffer) {
	buf.Printf("%v (", idx.Info)
	for i, col := range idx.Columns {
		if i != 0 {
			buf.Printf(", %v", col.Column)
		} else {
			buf.Printf("%v", col.Column)
		}
		if col.Length != nil {
			buf.Printf("(%v)", col.Length)
		}
	}
	buf.Printf(")")

	for _, opt := range idx.Options {
		buf.Printf(" %s", opt.Name)
		if opt.Name == "using" {
			buf.Printf(" %s", opt.Value.Val)
		} else {
			buf.Printf(" %v", opt.Value)
		}
	}
}

// IndexInfo describes the name and type of an index in a CREATE TABLE statement
type IndexInfo struct {
	Type      string
	Name      ColIdent
	Primary   bool
	Spatial   bool
	Unique    bool
	Fulltext  bool
	Clustered BoolVal
}

// Format formats the node.
func (ii *IndexInfo) Format(buf *nodeBuffer) {
	if ii.Primary {
		buf.Printf("%s", ii.Type)
	} else {
		buf.Printf("%s %v", ii.Type, ii.Name)
	}
}

type IndexColumnsOrExpression struct {
	IndexCols []IndexColumn
	IndexExpr Expr
}

// IndexColumn describes a column in an index definition with optional length
type IndexColumn struct {
	Column        ColIdent
	Length        *SQLVal
	Direction     string
	OperatorClass string
}

// LengthScaleOption is used for types that have an optional length
// and scale
type LengthScaleOption struct {
	Length *SQLVal
	Scale  *SQLVal
}

// IndexOption is used for trailing options for indexes: COMMENT, KEY_BLOCK_SIZE, USING
type IndexOption struct {
	Name  string
	Value *SQLVal
}

type IndexPartition struct {
	Name   string
	Column string
}

// ColumnKeyOption indicates whether or not the given column is defined as an
// index element and contains the type of the option
type ColumnKeyOption int

const (
	colKeyNone ColumnKeyOption = iota
	colKeyPrimary
	colKeySpatialKey
	colKeyUnique
	colKeyUniqueKey
	colKey
)

type IndexSpec struct {
	Name              ColIdent
	Type              ColIdent
	Unique            bool
	Primary           bool
	Constraint        bool
	Clustered         bool // for MSSQL
	ColumnStore       bool // for MSSQL
	Included          []ColIdent
	Where             *Where
	Options           []*IndexOption
	Partition         *IndexPartition // for MSSQL
	ConstraintOptions *ConstraintOptions
}

type ConstraintOptions struct {
	Deferrable        bool // for Postgres
	InitiallyDeferred bool // for Postgres
}

type ForeignKeyDefinition struct {
	ConstraintName    ColIdent
	IndexName         ColIdent
	IndexColumns      []ColIdent
	ReferenceName     TableName
	ReferenceColumns  []ColIdent
	OnDelete          ColIdent
	OnUpdate          ColIdent
	NotForReplication bool
	ConstraintOptions *ConstraintOptions
}

type Policy struct {
	Name       ColIdent
	Permissive Permissive
	Scope      []byte
	To         []ColIdent
	Using      *Where
	WithCheck  *Where
}

type Extension struct {
	Name string
}

type Schema struct {
	Name string
}

type Permissive string

// Show represents a show statement.
type Show struct {
	Type          string
	OnTable       TableName
	ShowTablesOpt *ShowTablesOpt
	Scope         string
}

// Format formats the node.
func (node *Show) Format(buf *nodeBuffer) {
	if node.Type == "tables" && node.ShowTablesOpt != nil {
		opt := node.ShowTablesOpt
		if opt.DbName != "" {
			if opt.Filter != nil {
				buf.Printf("show %s%stables from %s %v", opt.Extended, opt.Full, opt.DbName, opt.Filter)
			} else {
				buf.Printf("show %s%stables from %s", opt.Extended, opt.Full, opt.DbName)
			}
		} else {
			if opt.Filter != nil {
				buf.Printf("show %s%stables %v", opt.Extended, opt.Full, opt.Filter)
			} else {
				buf.Printf("show %s%stables", opt.Extended, opt.Full)
			}
		}
		return
	}
	if node.Scope == "" {
		buf.Printf("show %s", node.Type)
	} else {
		buf.Printf("show %s %s", node.Scope, node.Type)
	}
	if !node.OnTable.Name.isEmpty() {
		buf.Printf(" on %v", node.OnTable)
	}
}

// ShowTablesOpt is show tables option
type ShowTablesOpt struct {
	Extended string
	Full     string
	DbName   string
	Filter   *ShowFilter
}

// ShowFilter is show tables filter
type ShowFilter struct {
	Like   string
	Filter Expr
}

// Format formats the node.
func (node *ShowFilter) Format(buf *nodeBuffer) {
	if node.Like != "" {
		buf.Printf("like '%s'", node.Like)
	} else {
		buf.Printf("where %v", node.Filter)
	}
}

// Use represents a use statement.
type Use struct {
	DBName TableIdent
}

// Format formats the node.
func (node *Use) Format(buf *nodeBuffer) {
	if node.DBName.v != "" {
		buf.Printf("use %v", node.DBName)
	} else {
		buf.Printf("use")
	}
}

// Begin represents a Begin statement.
type Begin struct{}

// Format formats the node.
func (node *Begin) Format(buf *nodeBuffer) {
	buf.WriteString("begin")
}

// Commit represents a Commit statement.
type Commit struct{}

// Format formats the node.
func (node *Commit) Format(buf *nodeBuffer) {
	buf.WriteString("commit")
}

// Rollback represents a Rollback statement.
type Rollback struct{}

// Format formats the node.
func (node *Rollback) Format(buf *nodeBuffer) {
	buf.WriteString("rollback")
}

// OtherRead represents a DESCRIBE, or EXPLAIN statement.
// It should be used only as an indicator. It does not contain
// the full AST for the statement.
type OtherRead struct{}

// Format formats the node.
func (node *OtherRead) Format(buf *nodeBuffer) {
	buf.WriteString("otherread")
}

// OtherAdmin represents a misc statement that relies on ADMIN privileges,
// such as REPAIR, OPTIMIZE, or TRUNCATE statement.
// It should be used only as an indicator. It does not contain
// the full AST for the statement.
type OtherAdmin struct{}

// Format formats the node.
func (node *OtherAdmin) Format(buf *nodeBuffer) {
	buf.WriteString("otheradmin")
}

// SetOption represents a SET statement that specifies option in SQL Server.
type SetBoolOption struct {
	OptionNames []string
	Value       *SQLVal
}

// Format formats the node.
func (node *SetBoolOption) Format(buf *nodeBuffer) {
	buf.Printf("set %s", strings.Join(node.OptionNames, ", "))
	if node.Value.Val[0] == 't' {
		buf.WriteString(" on")
	} else {
		buf.WriteString(" off")
	}
}

// Comments represents a list of comments.
type Comments [][]byte

// Format formats the node.
func (node Comments) Format(buf *nodeBuffer) {
	for _, c := range node {
		buf.Printf("%s ", c)
	}
}

type View struct {
	Type         string
	SecurityType string
	Name         TableName
	Definition   SelectStatement
}

type Trigger struct {
	Name      ColIdent
	TableName TableName
	Time      string
	Event     []string
	Body      []Statement
}

type Type struct {
	Name TableName // workaround: using TableName to handle schema
	Type ColumnType
}

type Comment struct {
	ObjectType string
	Object     string
	Comment    string
}

// SelectExprs represents SELECT expressions.
type SelectExprs []SelectExpr

// Format formats the node.
func (node SelectExprs) Format(buf *nodeBuffer) {
	var prefix string
	for _, n := range node {
		buf.Printf("%s%v", prefix, n)
		prefix = ", "
	}
}

// SelectExpr represents a SELECT expression.
type SelectExpr interface {
	iSelectExpr()
	SQLNode
}

func (*StarExpr) iSelectExpr()    {}
func (*AliasedExpr) iSelectExpr() {}

// StarExpr defines a '*' or 'table.*' expression.
type StarExpr struct {
	TableName TableName
}

// Format formats the node.
func (node *StarExpr) Format(buf *nodeBuffer) {
	if !node.TableName.isEmpty() {
		buf.Printf("%v.", node.TableName)
	}
	buf.Printf("*")
}

// AliasedExpr defines an aliased SELECT expression.
type AliasedExpr struct {
	Expr Expr
	As   ColIdent
}

// Format formats the node.
func (node *AliasedExpr) Format(buf *nodeBuffer) {
	buf.Printf("%v", node.Expr)
	if !node.As.isEmpty() {
		buf.Printf(" as %v", node.As)
	}
}

// Columns represents an insert column list.
type Columns []ColIdent

// Format formats the node.
func (node Columns) Format(buf *nodeBuffer) {
	if node == nil {
		return
	}
	prefix := "("
	for _, n := range node {
		buf.Printf("%s%v", prefix, n)
		prefix = ", "
	}
	buf.WriteString(")")
}

// Partitions is a type alias for Columns so we can handle printing efficiently
type Partitions Columns

// Format formats the node
func (node Partitions) Format(buf *nodeBuffer) {
	if node == nil {
		return
	}
	prefix := " partition ("
	for _, n := range node {
		buf.Printf("%s%v", prefix, n)
		prefix = ", "
	}
	buf.WriteString(")")
}

// TableExprs represents a list of table expressions.
type TableExprs []TableExpr

// Format formats the node.
func (node TableExprs) Format(buf *nodeBuffer) {
	var prefix string
	for _, n := range node {
		buf.Printf("%s%v", prefix, n)
		prefix = ", "
	}
}

// TableExpr represents a table expression.
type TableExpr interface {
	iTableExpr()
	SQLNode
}

func (*AliasedTableExpr) iTableExpr() {}
func (*ParenTableExpr) iTableExpr()   {}
func (*JoinTableExpr) iTableExpr()    {}

// AliasedTableExpr represents a table expression
// coupled with an optional alias or index hint.
// If As is empty, no alias was used.
type AliasedTableExpr struct {
	Expr       SimpleTableExpr
	Partitions Partitions
	As         TableIdent
	TableHints []string
	IndexHints *IndexHints
}

// Format formats the node.
func (node *AliasedTableExpr) Format(buf *nodeBuffer) {
	buf.Printf("%v%v", node.Expr, node.Partitions)
	if !node.As.isEmpty() {
		buf.Printf(" as %v", node.As)
	}
	if len(node.TableHints) != 0 {
		buf.Printf(" with(%s)", strings.Join(node.TableHints, ", "))
	}
	if node.IndexHints != nil {
		// Hint node provides the space padding.
		buf.Printf("%v", node.IndexHints)
	}
}

// SimpleTableExpr represents a simple table expression.
type SimpleTableExpr interface {
	iSimpleTableExpr()
	SQLNode
}

func (TableName) iSimpleTableExpr() {}
func (*Subquery) iSimpleTableExpr() {}

// TableNames is a list of TableName.
type TableNames []TableName

// Format formats the node.
func (node TableNames) Format(buf *nodeBuffer) {
	var prefix string
	for _, n := range node {
		buf.Printf("%s%v", prefix, n)
		prefix = ", "
	}
}

// TableName represents a table name: [Name] or [Schema].[Name]
type TableName struct {
	Schema TableIdent
	Name   TableIdent
}

// Format formats the node.
func (node TableName) Format(buf *nodeBuffer) {
	if node.isEmpty() {
		return
	}
	if !node.Schema.isEmpty() {
		buf.Printf("%v.", node.Schema)
	}
	buf.Printf("%v", node.Name)
}

// isEmpty returns true if TableName is nil or empty.
func (node TableName) isEmpty() bool {
	// If Name is empty, Schema is also empty.
	return node.Name.isEmpty()
}

// toViewName returns a TableName acceptable for use as a VIEW. VIEW names are
// always lowercase, so toViewName lowercasese the name. Databases are case-sensitive
// so Schema is left untouched.
func (node TableName) toViewName() TableName {
	return TableName{
		Schema: node.Schema,
		Name:   NewTableIdent(strings.ToLower(node.Name.v)),
	}
}

// ParenTableExpr represents a parenthesized list of TableExpr.
type ParenTableExpr struct {
	Exprs TableExprs
}

// Format formats the node.
func (node *ParenTableExpr) Format(buf *nodeBuffer) {
	buf.Printf("(%v)", node.Exprs)
}

// JoinCondition represents the join conditions (either a ON or USING clause)
// of a JoinTableExpr.
type JoinCondition struct {
	On    Expr
	Using Columns
}

// Format formats the node.
func (node JoinCondition) Format(buf *nodeBuffer) {
	if node.On != nil {
		buf.Printf(" on %v", node.On)
	}
	if node.Using != nil {
		buf.Printf(" using %v", node.Using)
	}
}

// JoinTableExpr represents a TableExpr that's a JOIN operation.
type JoinTableExpr struct {
	LeftExpr  TableExpr
	Join      string
	RightExpr TableExpr
	Condition JoinCondition
}

// JoinTableExpr.Join
const (
	JoinStr             = "join"
	StraightJoinStr     = "straight_join"
	LeftJoinStr         = "left join"
	RightJoinStr        = "right join"
	NaturalJoinStr      = "natural join"
	NaturalLeftJoinStr  = "natural left join"
	NaturalRightJoinStr = "natural right join"
)

// Format formats the node.
func (node *JoinTableExpr) Format(buf *nodeBuffer) {
	buf.Printf("%v %s %v%v", node.LeftExpr, node.Join, node.RightExpr, node.Condition)
}

// IndexHints represents a list of index hints.
type IndexHints struct {
	Type    string
	Indexes []ColIdent
}

// Index hints.
const (
	UseStr    = "use "
	IgnoreStr = "ignore "
	ForceStr  = "force "
)

// Format formats the node.
func (node *IndexHints) Format(buf *nodeBuffer) {
	buf.Printf(" %sindex ", node.Type)
	prefix := "("
	for _, n := range node.Indexes {
		buf.Printf("%s%v", prefix, n)
		prefix = ", "
	}
	buf.Printf(")")
}

// Where represents a WHERE or HAVING clause.
type Where struct {
	Type string
	Expr Expr
}

// Where.Type
const (
	WhereStr  = "where"
	HavingStr = "having"
)

// NewWhere creates a WHERE or HAVING clause out
// of a Expr. If the expression is nil, it returns nil.
func NewWhere(typ string, expr Expr) *Where {
	if expr == nil {
		return nil
	}
	return &Where{Type: typ, Expr: expr}
}

// Format formats the node.
func (node *Where) Format(buf *nodeBuffer) {
	if node == nil || node.Expr == nil {
		return
	}
	buf.Printf(" %s %v", node.Type, node.Expr)
}

// Expr represents an expression.
type Expr interface {
	iExpr()
	SQLNode
}

func (*AndExpr) iExpr()             {}
func (*OrExpr) iExpr()              {}
func (*NotExpr) iExpr()             {}
func (*ParenExpr) iExpr()           {}
func (*ComparisonExpr) iExpr()      {}
func (*RangeCond) iExpr()           {}
func (*IsExpr) iExpr()              {}
func (*ExistsExpr) iExpr()          {}
func (*SQLVal) iExpr()              {}
func (*NullVal) iExpr()             {}
func (BoolVal) iExpr()              {}
func (*ColName) iExpr()             {}
func (*NewQualifierColName) iExpr() {}
func (ValTuple) iExpr()             {}
func (*Subquery) iExpr()            {}
func (ListArg) iExpr()              {}
func (*BinaryExpr) iExpr()          {}
func (*UnaryExpr) iExpr()           {}
func (*IntervalExpr) iExpr()        {}
func (*CollateExpr) iExpr()         {}
func (*FuncExpr) iExpr()            {}
func (*CaseExpr) iExpr()            {}
func (*ValuesFuncExpr) iExpr()      {}
func (*CastExpr) iExpr()            {}
func (*ConvertExpr) iExpr()         {}
func (*SubstrExpr) iExpr()          {}
func (*ConvertUsingExpr) iExpr()    {}
func (*MatchExpr) iExpr()           {}
func (*GroupConcatExpr) iExpr()     {}
func (*OverExpr) iExpr()            {}
func (*Default) iExpr()             {}
func (*ArrayConstructor) iExpr()    {}
func (*FuncCallExpr) iExpr()        {}
func (*NextSeqValExpr) iExpr()      {}

// Exprs represents a list of value expressions.
// It's not a valid expression because it's not parenthesized.
type Exprs []Expr

// Format formats the node.
func (node Exprs) Format(buf *nodeBuffer) {
	var prefix string
	for _, n := range node {
		buf.Printf("%s%v", prefix, n)
		prefix = ", "
	}
}

// AndExpr represents an AND expression.
type AndExpr struct {
	Left, Right Expr
}

// Format formats the node.
func (node *AndExpr) Format(buf *nodeBuffer) {
	buf.Printf("%v and %v", node.Left, node.Right)
}

// OrExpr represents an OR expression.
type OrExpr struct {
	Left, Right Expr
}

// Format formats the node.
func (node *OrExpr) Format(buf *nodeBuffer) {
	buf.Printf("%v or %v", node.Left, node.Right)
}

// NotExpr represents a NOT expression.
type NotExpr struct {
	Expr Expr
}

// Format formats the node.
func (node *NotExpr) Format(buf *nodeBuffer) {
	buf.Printf("not %v", node.Expr)
}

// ParenExpr represents a parenthesized boolean expression.
type ParenExpr struct {
	Expr Expr
}

// Format formats the node.
func (node *ParenExpr) Format(buf *nodeBuffer) {
	buf.Printf("(%v)", node.Expr)
}

// ComparisonExpr represents a two-value comparison expression.
type ComparisonExpr struct {
	Operator    string
	Left, Right Expr
	Escape      Expr
	All, Any    bool
}

// ComparisonExpr.Operator
const (
	EqualStr             = "="
	LessThanStr          = "<"
	GreaterThanStr       = ">"
	LessEqualStr         = "<="
	GreaterEqualStr      = ">="
	NotEqualStr          = "!="
	NullSafeEqualStr     = "<=>"
	InStr                = "in"
	NotInStr             = "not in"
	LikeStr              = "like"
	NotLikeStr           = "not like"
	RegexpStr            = "regexp"
	NotRegexpStr         = "not regexp"
	JSONExtractOp        = "->"
	JSONUnquoteExtractOp = "->>"
	PosixRegexStr        = "~"
	PosixRegexCiStr      = "~*"
	PosixNotRegexStr     = "!~"
	PosixNotRegexCiStr   = "!~*"
)

// Format formats the node.
func (node *ComparisonExpr) Format(buf *nodeBuffer) {
	if node.All {
		buf.Printf("%v %s ALL (%v)", node.Left, node.Operator, node.Right)
	} else if node.Any {
		buf.Printf("%v %s ANY (%v)", node.Left, node.Operator, node.Right)
	} else {
		buf.Printf("%v %s %v", node.Left, node.Operator, node.Right)
	}
	if node.Escape != nil {
		buf.Printf(" escape %v", node.Escape)
	}
}

// RangeCond represents a BETWEEN or a NOT BETWEEN expression.
type RangeCond struct {
	Operator string
	Left     Expr
	From, To Expr
}

// RangeCond.Operator
const (
	BetweenStr    = "between"
	NotBetweenStr = "not between"
)

// Format formats the node.
func (node *RangeCond) Format(buf *nodeBuffer) {
	buf.Printf("%v %s %v and %v", node.Left, node.Operator, node.From, node.To)
}

// IsExpr represents an IS ... or an IS NOT ... expression.
type IsExpr struct {
	Operator string
	Expr     Expr
}

// IsExpr.Operator
const (
	IsNullStr       = "is null"
	IsNotNullStr    = "is not null"
	IsTrueStr       = "is true"
	IsNotTrueStr    = "is not true"
	IsFalseStr      = "is false"
	IsNotFalseStr   = "is not false"
	IsUnknownStr    = "is unknown"
	IsNotUnknownStr = "is not unknown"
)

// Format formats the node.
func (node *IsExpr) Format(buf *nodeBuffer) {
	buf.Printf("%v %s", node.Expr, node.Operator)
}

// ExistsExpr represents an EXISTS expression.
type ExistsExpr struct {
	Subquery *Subquery
}

// Format formats the node.
func (node *ExistsExpr) Format(buf *nodeBuffer) {
	buf.Printf("exists %v", node.Subquery)
}

// ValType specifies the type for SQLVal.
type ValType int

// These are the possible Valtype values.
// HexNum represents a 0x... value. It cannot
// be treated as a simple value because it can
// be interpreted differently depending on the
// context.
const (
	StrVal = ValType(iota)
	IntVal
	FloatVal
	HexNum
	HexVal
	ValArg
	BitVal
	ValBool
	UnicodeStrVal
)

// SQLVal represents a single value.
type SQLVal struct {
	Type ValType
	Val  []byte
}

// NewStrVal builds a new StrVal.
func NewStrVal(in []byte) *SQLVal {
	return &SQLVal{Type: StrVal, Val: in}
}

// NewIntVal builds a new IntVal.
func NewIntVal(in []byte) *SQLVal {
	return &SQLVal{Type: IntVal, Val: in}
}

// NewFloatVal builds a new FloatVal.
func NewFloatVal(in []byte) *SQLVal {
	return &SQLVal{Type: FloatVal, Val: in}
}

// NewHexNum builds a new HexNum.
func NewHexNum(in []byte) *SQLVal {
	return &SQLVal{Type: HexNum, Val: in}
}

// NewHexVal builds a new HexVal.
func NewHexVal(in []byte) *SQLVal {
	return &SQLVal{Type: HexVal, Val: in}
}

// NewBitVal builds a new BitVal containing a bit literal.
func NewBitVal(in []byte) *SQLVal {
	return &SQLVal{Type: BitVal, Val: in}
}

// NewValArg builds a new ValArg.
func NewValArg(in []byte) *SQLVal {
	return &SQLVal{Type: ValArg, Val: in}
}

func NewBoolSQLVal(in bool) *SQLVal {
	return &SQLVal{Type: ValBool, Val: []byte(fmt.Sprintf("%t", in))}
}

// NewUnicode builds a new UnicodeStrVal.
func NewUnicodeStrVal(in []byte) *SQLVal {
	return &SQLVal{Type: UnicodeStrVal, Val: in}
}

func NewValArgWithOpt(in []byte, opt *SQLVal) *SQLVal {
	if opt != nil {
		combined := string(in) + "(" + string(opt.Val) + ")"
		return NewValArg([]byte(combined))
	} else {
		return NewValArg(in)
	}
}

// Format formats the node.
func (node *SQLVal) Format(buf *nodeBuffer) {
	switch node.Type {
	case StrVal:
		encodeSQLBytes(node.Val, buf)
	case UnicodeStrVal:
		buf.WriteRune('N')
		encodeSQLBytes(node.Val, buf)
	case IntVal, FloatVal, HexNum:
		buf.Printf("%s", []byte(node.Val))
	case HexVal:
		buf.Printf("X'%s'", []byte(node.Val))
	case BitVal:
		buf.Printf("B'%s'", []byte(node.Val))
	case ValArg:
		buf.WriteString(string(node.Val))
	case ValBool:
		buf.Printf("%t", node.Val)
	default:
		panic("unexpected")
	}
}

func encodeSQLBytes(val []byte, buf *nodeBuffer) {
	buf.WriteByte('\'')
	for _, ch := range val {
		if encodedChar := sqlEncodeMap[ch]; encodedChar == dontEscape {
			buf.WriteByte(ch)
		} else {
			buf.WriteByte('\\')
			buf.WriteByte(encodedChar)
		}
	}
	buf.WriteByte('\'')
}

// NullVal represents a NULL value.
type NullVal struct{}

// Format formats the node.
func (node *NullVal) Format(buf *nodeBuffer) {
	buf.Printf("null")
}

// BoolVal is true or false.
type BoolVal bool

func NewBoolVal(flag bool) *BoolVal {
	val := BoolVal(flag)
	return &val
}

// Format formats the node.
func (node BoolVal) Format(buf *nodeBuffer) {
	if node {
		buf.Printf("true")
	} else {
		buf.Printf("false")
	}
}

// ColName represents a column name.
type ColName struct {
	// Metadata is not populated by the parser.
	// It's a placeholder for analyzers to store
	// additional data, typically info about which
	// table or column this node references.
	Metadata  interface{}
	Name      ColIdent
	Qualifier TableName
}

// Format formats the node.
func (node *ColName) Format(buf *nodeBuffer) {
	if !node.Qualifier.isEmpty() {
		buf.Printf("%v.", node.Qualifier)
	}
	buf.Printf("%v", node.Name)
}

// NewQualifierColName represents a column name with NEW qualifier.
type NewQualifierColName struct {
	Name ColIdent
}

// Format formats the node.
func (node *NewQualifierColName) Format(buf *nodeBuffer) {
	// We don't have to backtick NEW qualifier.
	buf.Printf("NEW.%s", node.Name.String())
}

// ColTuple represents a list of column values.
// It can be ValTuple, Subquery, ListArg.
type ColTuple interface {
	iColTuple()
	Expr
}

func (ValTuple) iColTuple()  {}
func (*Subquery) iColTuple() {}
func (ListArg) iColTuple()   {}

// ValTuple represents a tuple of actual values.
type ValTuple Exprs

// Format formats the node.
func (node ValTuple) Format(buf *nodeBuffer) {
	buf.Printf("(%v)", Exprs(node))
}

// Subquery represents a subquery.
type Subquery struct {
	Select SelectStatement
}

// Format formats the node.
func (node *Subquery) Format(buf *nodeBuffer) {
	buf.Printf("(%v)", node.Select)
}

// ListArg represents a named list argument.
type ListArg []byte

// Format formats the node.
func (node ListArg) Format(buf *nodeBuffer) {
	buf.WriteString(string(node))
}

// BinaryExpr represents a binary value expression.
type BinaryExpr struct {
	Operator    string
	Left, Right Expr
}

// BinaryExpr.Operator
const (
	BitAndStr     = "&"
	BitOrStr      = "|"
	BitXorStr     = "^"
	PlusStr       = "+"
	MinusStr      = "-"
	MultStr       = "*"
	DivStr        = "/"
	IntDivStr     = "div"
	ModStr        = "%"
	ShiftLeftStr  = "<<"
	ShiftRightStr = ">>"
)

// Format formats the node.
func (node *BinaryExpr) Format(buf *nodeBuffer) {
	buf.Printf("%v %s %v", node.Left, node.Operator, node.Right)
}

// UnaryExpr represents a unary value expression.
type UnaryExpr struct {
	Operator string
	Expr     Expr
}

// UnaryExpr.Operator
const (
	UPlusStr   = "+"
	UMinusStr  = "-"
	TildaStr   = "~"
	BangStr    = "!"
	BinaryStr  = "binary "
	UBinaryStr = "_binary "
)

// Format formats the node.
func (node *UnaryExpr) Format(buf *nodeBuffer) {
	if _, unary := node.Expr.(*UnaryExpr); unary {
		buf.Printf("%s %v", node.Operator, node.Expr)
		return
	}
	buf.Printf("%s%v", node.Operator, node.Expr)
}

// IntervalExpr represents a date-time INTERVAL expression.
type IntervalExpr struct {
	Expr Expr
	Unit string
}

// Format formats the node.
func (node *IntervalExpr) Format(buf *nodeBuffer) {
	buf.Printf("interval %v %s", node.Expr, node.Unit)
}

// CollateExpr represents dynamic collate operator.
type CollateExpr struct {
	Expr    Expr
	Charset string
}

// Format formats the node.
func (node *CollateExpr) Format(buf *nodeBuffer) {
	buf.Printf("%v collate %s", node.Expr, node.Charset)
}

// FuncExpr represents a function call that takes SelectExprs.
type FuncExpr struct {
	Qualifier TableIdent
	Name      ColIdent
	Distinct  bool
	Exprs     SelectExprs
	Over      *OverExpr
}

// Format formats the node.
func (node *FuncExpr) Format(buf *nodeBuffer) {
	var distinct string
	if node.Distinct {
		distinct = "distinct "
	}
	if !node.Qualifier.isEmpty() {
		buf.Printf("%v.", node.Qualifier)
	}
	// Function names should not be back-quoted even
	// if they match a reserved word. So, print the
	// name as is.
	buf.Printf("%s(%s%v)%v", node.Name.String(), distinct, node.Exprs, node.Over)
}

// FuncCallExpr represents a function call that takes Exprs.
type FuncCallExpr struct {
	Name  ColIdent
	Exprs Exprs
}

func (node *FuncCallExpr) Format(buf *nodeBuffer) {
	// Function names should not be back-quoted even
	// if they match a reserved word. So, print the
	// name as is.
	buf.Printf("%s(%v)", node.Name.String(), node.Exprs)
}

// GroupConcatExpr represents a call to GROUP_CONCAT
type GroupConcatExpr struct {
	Distinct  string
	Exprs     SelectExprs
	OrderBy   OrderBy
	Separator string
}

// Format formats the node
func (node *GroupConcatExpr) Format(buf *nodeBuffer) {
	buf.Printf("group_concat(%s%v%v%s)", node.Distinct, node.Exprs, node.OrderBy, node.Separator)
}

// OverExpr represents a call to OVER
type OverExpr struct {
	PartitionBy PartitionBy
	OrderBy     OrderBy
}

// Format formats the node
func (node *OverExpr) Format(buf *nodeBuffer) {
	if node == nil {
		return
	}
	buf.Printf(" over(%v%v)", node.PartitionBy, node.OrderBy)
}

// ValuesFuncExpr represents a function call.
type ValuesFuncExpr struct {
	Name *ColName
}

// Format formats the node.
func (node *ValuesFuncExpr) Format(buf *nodeBuffer) {
	buf.Printf("values(%v)", node.Name)
}

// SubstrExpr represents a call to SubstrExpr(column, value_expression) or SubstrExpr(column, value_expression,value_expression)
// also supported syntax SubstrExpr(column from value_expression for value_expression)
type SubstrExpr struct {
	Name SelectExpr
	From Expr
	To   Expr
}

// Format formats the node.
func (node *SubstrExpr) Format(buf *nodeBuffer) {
	if node.To == nil {
		buf.Printf("substr(%v, %v)", node.Name, node.From)
	} else {
		buf.Printf("substr(%v, %v, %v)", node.Name, node.From, node.To)
	}
}

// CastExpr represents expr::type
type CastExpr struct {
	Expr Expr
	Type *ConvertType
}

func (node *CastExpr) Format(buf *nodeBuffer) {
	buf.Printf("%v::%v", node.Expr, node.Type)
}

// ConvertExpr represents a call to CONVERT(expr, type)
// or it's equivalent CAST(expr AS type). Both are rewritten to the former.
type ConvertExpr struct {
	Expr Expr
	Type *ConvertType
}

func (node *ConvertExpr) Format(buf *nodeBuffer) {
	buf.Printf("convert(%v, %v)", node.Expr, node.Type)
}

// ConvertUsingExpr represents a call to CONVERT(expr USING charset).
type ConvertUsingExpr struct {
	Expr Expr
	Type string
}

func (node *ConvertUsingExpr) Format(buf *nodeBuffer) {
	buf.Printf("convert(%v using %s)", node.Expr, node.Type)
}

// ConvertType represents the type in call to CONVERT(expr, type)
type ConvertType struct {
	Type     string
	Length   *SQLVal
	Scale    *SQLVal
	Operator string
	Charset  string
}

// this string is "character set" and this comment is required
const (
	CharacterSetStr = " character set"
)

// Format formats the node.
func (node *ConvertType) Format(buf *nodeBuffer) {
	buf.Printf("%s", node.Type)
	if node.Length != nil {
		buf.Printf("(%v", node.Length)
		if node.Scale != nil {
			buf.Printf(", %v", node.Scale)
		}
		buf.Printf(")")
	}
	if node.Charset != "" {
		buf.Printf("%s %s", node.Operator, node.Charset)
	}
}

// MatchExpr represents a call to the MATCH function
type MatchExpr struct {
	Columns SelectExprs
	Expr    Expr
	Option  string
}

// MatchExpr.Option
const (
	BooleanModeStr                           = " in boolean mode"
	NaturalLanguageModeStr                   = " in natural language mode"
	NaturalLanguageModeWithQueryExpansionStr = " in natural language mode with query expansion"
	QueryExpansionStr                        = " with query expansion"
)

// Format formats the node
func (node *MatchExpr) Format(buf *nodeBuffer) {
	buf.Printf("match(%v) against (%v%s)", node.Columns, node.Expr, node.Option)
}

// CaseExpr represents a CASE expression.
type CaseExpr struct {
	Expr  Expr
	Whens []*When
	Else  Expr
}

// Format formats the node.
func (node *CaseExpr) Format(buf *nodeBuffer) {
	buf.Printf("case ")
	if node.Expr != nil {
		buf.Printf("%v ", node.Expr)
	}
	for _, when := range node.Whens {
		buf.Printf("%v ", when)
	}
	if node.Else != nil {
		buf.Printf("else %v ", node.Else)
	}
	buf.Printf("end")
}

// Default represents a DEFAULT expression.
type Default struct {
	ColName string
}

// Format formats the node.
func (node *Default) Format(buf *nodeBuffer) {
	buf.Printf("default")
	if node.ColName != "" {
		buf.Printf("(%s)", node.ColName)
	}
}

// NextSeqVal represents a NEXT VALUE FOR expression in SQL Server.
type NextSeqValExpr struct {
	SequenceName TableIdent
}

// Format formats the node.
func (node *NextSeqValExpr) Format(buf *nodeBuffer) {
	buf.Printf("next value for %v", node.SequenceName)
}

// When represents a WHEN sub-expression.
type When struct {
	Cond Expr
	Val  Expr
}

// Format formats the node.
func (node *When) Format(buf *nodeBuffer) {
	buf.Printf("when %v then %v", node.Cond, node.Val)
}

// GroupBy represents a GROUP BY clause.
type GroupBy []Expr

// Format formats the node.
func (node GroupBy) Format(buf *nodeBuffer) {
	prefix := " group by "
	for _, n := range node {
		buf.Printf("%s%v", prefix, n)
		prefix = ", "
	}
}

// OrderBy represents an ORDER By clause.
type OrderBy []*Order

// Format formats the node.
func (node OrderBy) Format(buf *nodeBuffer) {
	prefix := " order by "
	for _, n := range node {
		buf.Printf("%s%v", prefix, n)
		prefix = ", "
	}
}

// Order represents an ordering expression.
type Order struct {
	Expr      Expr
	Direction string
}

// Order.Direction and IndexColumn.Direction
const (
	AscScr  = "asc"
	DescScr = "desc"
)

// Format formats the node.
func (node *Order) Format(buf *nodeBuffer) {
	if node, ok := node.Expr.(*NullVal); ok {
		buf.Printf("%v", node)
		return
	}
	if node, ok := node.Expr.(*FuncExpr); ok {
		if node.Name.lowered() == "rand" {
			buf.Printf("%v", node)
			return
		}
	}

	buf.Printf("%v %s", node.Expr, node.Direction)
}

// PartitionBy represents a PARTITION BY clause.
type PartitionBy []*Partition

// Format formats the node.
func (node PartitionBy) Format(buf *nodeBuffer) {
	prefix := "partition by "
	for _, n := range node {
		buf.Printf("%s%v", prefix, n)
		prefix = ", "
	}
}

// Partition represents an partitions expression.
type Partition struct {
	Expr Expr
}

// Format formats the node.
func (node *Partition) Format(buf *nodeBuffer) {
	if node == nil {
		return
	}
	buf.Printf("%v", node.Expr)
}

// Limit represents a LIMIT clause.
type Limit struct {
	Offset, Rowcount Expr
}

// Format formats the node.
func (node *Limit) Format(buf *nodeBuffer) {
	if node == nil {
		return
	}
	buf.Printf(" limit ")
	if node.Offset != nil {
		buf.Printf("%v, ", node.Offset)
	}
	buf.Printf("%v", node.Rowcount)
}

// Values represents a VALUES clause.
type Values []ValTuple

// Format formats the node.
func (node Values) Format(buf *nodeBuffer) {
	prefix := "values "
	for _, n := range node {
		buf.Printf("%s%v", prefix, n)
		prefix = ", "
	}
}

// UpdateExprs represents a list of update expressions.
type UpdateExprs []*UpdateExpr

// Format formats the node.
func (node UpdateExprs) Format(buf *nodeBuffer) {
	var prefix string
	for _, n := range node {
		buf.Printf("%s%v", prefix, n)
		prefix = ", "
	}
}

// UpdateExpr represents an update expression.
type UpdateExpr struct {
	Name *ColName
	Expr Expr
}

// Format formats the node.
func (node *UpdateExpr) Format(buf *nodeBuffer) {
	buf.Printf("%v = %v", node.Name, node.Expr)
}

// SetExprs represents a list of set expressions.
type SetExprs []*SetExpr

// Format formats the node.
func (node SetExprs) Format(buf *nodeBuffer) {
	var prefix string
	for _, n := range node {
		buf.Printf("%s%v", prefix, n)
		prefix = ", "
	}
}

// SetExpr represents a set expression.
type SetExpr struct {
	Name ColIdent
	Expr Expr
}

// Format formats the node.
func (node *SetExpr) Format(buf *nodeBuffer) {
	// We don't have to backtick set variable names.
	if node.Name.equalString("charset") || node.Name.equalString("names") {
		buf.Printf("%s %v", node.Name.String(), node.Expr)
	} else {
		buf.Printf("%s = %v", node.Name.String(), node.Expr)
	}
}

// OnDup represents an ON DUPLICATE KEY clause.
type OnDup UpdateExprs

// Format formats the node.
func (node OnDup) Format(buf *nodeBuffer) {
	if node == nil {
		return
	}
	buf.Printf(" on duplicate key update %v", UpdateExprs(node))
}

// ColIdent is a case insensitive SQL identifier. It will be escaped with
// backquotes if necessary.
type ColIdent struct {
	// This artifact prevents this struct from being compared
	// with itself. It consumes no space as long as it's not the
	// last field in the struct.
	_            [0]struct{ _ []byte }
	val          string
	loweredCache string
}

// NewColIdent makes a new ColIdent.
func NewColIdent(str string) ColIdent {
	return ColIdent{
		val: str,
	}
}

// Format formats the node.
func (node ColIdent) Format(buf *nodeBuffer) {
	formatID(buf, node.val, node.lowered())
}

// isEmpty returns true if the name is empty.
func (node ColIdent) isEmpty() bool {
	return node.val == ""
}

// String returns the unescaped column name. It must
// not be used for SQL generation. Use parser.String
// instead. The Stringer conformance is for usage
// in templates.
func (node ColIdent) String() string {
	return node.val
}

// lowered returns a lower-cased column name.
// This function should generally be used only for optimizing
// comparisons.
func (node ColIdent) lowered() string {
	if node.val == "" {
		return ""
	}
	if node.loweredCache == "" {
		node.loweredCache = strings.ToLower(node.val)
	}
	return node.loweredCache
}

// equalString performs a case-insensitive compare with str.
func (node ColIdent) equalString(str string) bool {
	return node.lowered() == strings.ToLower(str)
}

type DeclareType int

const (
	declareVariable DeclareType = iota
	declareCursor
)

type Declare struct {
	Type      DeclareType
	Variables []*LocalVariable
	Cursor    *CursorDefinition
}

func (node *Declare) Format(buf *nodeBuffer) {
	var prefix string
	buf.Printf("declare\n")
	switch node.Type {
	case declareVariable:
		for _, n := range node.Variables {
			buf.Printf("%s%v", prefix, n)
			prefix = ",\n"
		}
	case declareCursor:
		buf.Printf("%v", node.Cursor)
	}
}

type LocalVariable struct {
	Name     ColIdent
	DataType ColumnType
}

func (node *LocalVariable) Format(buf *nodeBuffer) {
	buf.Printf("%v %v", node.Name, &node.DataType)
}

type CursorDefinition struct {
	Name   ColIdent
	Scroll bool
	Select SelectStatement
}

func (node *CursorDefinition) Format(buf *nodeBuffer) {
	var scrollStr string
	if node.Scroll {
		scrollStr = " scroll"
	}
	buf.Printf("%v%s cursor for\n%v", node.Name, scrollStr, node.Select)
}

const (
	OpenStr       = "open"
	CloseStr      = "close"
	DeallocateStr = "deallocate"
	FetchStr      = "fetch"
)

type Cursor struct {
	Action     string
	Fetch      string
	CursorName ColIdent
	Into       ColIdent
}

func (node *Cursor) Format(buf *nodeBuffer) {
	if node.Action == FetchStr {
		fetch := " "
		if node.Fetch != "" {
			fetch = fmt.Sprintf(" %s from", node.Fetch)
		}
		var into string
		if !node.Into.isEmpty() {
			into = fmt.Sprintf(" into %s", node.Into.lowered())
		}
		buf.Printf("%s%s %v%s", node.Action, fetch, node.CursorName, into)
	} else {
		buf.Printf("%s %v", node.Action, node.CursorName)
	}
}

type BeginEnd struct {
	Statements []Statement
}

func (node *BeginEnd) Format(buf *nodeBuffer) {
	buf.Printf("begin")
	for _, stmt := range node.Statements {
		buf.Printf("\n%v;", stmt)
	}
	buf.Printf("\nend")
}

type While struct {
	Condition  Expr
	Statements []Statement
	Keyword    string
}

func (node *While) Format(buf *nodeBuffer) {
	buf.Printf("while %v", node.Condition)
	var endKeyword string
	switch node.Keyword {
	case "begin":
		buf.Printf("\nbegin")
		endKeyword = "\nend"
	}
	for _, stmt := range node.Statements {
		buf.Printf("\n%v", stmt)
	}
	buf.Printf(endKeyword)
}

type If struct {
	Condition      Expr
	IfStatements   []Statement
	ElseStatements []Statement
	Keyword        string
}

func (node *If) Format(buf *nodeBuffer) {
	buf.Printf("if %v", node.Condition)
	var beginKeyword string
	var endKeyword string
	switch node.Keyword {
	case "begin":
		beginKeyword = "\nbegin"
		endKeyword = "\nend"
	case "then":
		beginKeyword = " then"
		endKeyword = ";\nend if"
	}
	buf.Printf(beginKeyword)
	for _, stmt := range node.IfStatements {
		buf.Printf("\n%v", stmt)
	}
	buf.Printf(endKeyword)
	if node.ElseStatements != nil {
		buf.Printf("\nelse%s", beginKeyword)
		for _, stmt := range node.ElseStatements {
			buf.Printf("\n%v", stmt)
		}
		buf.Printf(endKeyword)
	}
}

// TableIdent is a case sensitive SQL identifier.
// Escaped by Format() if necessary.
type TableIdent struct {
	v string
}

// NewTableIdent creates a new TableIdent.
func NewTableIdent(str string) TableIdent {
	return TableIdent{v: str}
}

// Format formats the node.
func (node TableIdent) Format(buf *nodeBuffer) {
	formatID(buf, node.v, strings.ToLower(node.v))
}

// isEmpty returns true if TabIdent is empty.
func (node TableIdent) isEmpty() bool {
	return node.v == ""
}

// String returns the unescaped table name. It must
// not be used for SQL generation. Use parser.String
// instead. The Stringer conformance is for usage
// in templates.
func (node TableIdent) String() string {
	return node.v
}

func formatID(buf *nodeBuffer, original, lowered string) {
	isDbSystemVariable := false
	if len(original) > 1 && original[:2] == "@@" {
		isDbSystemVariable = true
	}

	for i, c := range original {
		if !isLetter(uint16(c)) && (!isDbSystemVariable || !isCarat(uint16(c))) {
			if i == 0 || !isDigit(uint16(c)) {
				goto mustEscape
			}
		}
	}
	buf.Printf("%s", original)
	return

mustEscape:
	buf.WriteByte('`')
	for _, c := range original {
		buf.WriteRune(c)
		if c == '`' {
			buf.WriteByte('`')
		}
	}
	buf.WriteByte('`')
}

type ArrayConstructor struct {
	Elements ArrayElements
}

func (node *ArrayConstructor) Format(buf *nodeBuffer) {
	buf.Printf("ARRAY[%v]", node.Elements)
}

type ArrayElements []ArrayElement

func (node ArrayElements) Format(buf *nodeBuffer) {
	for i, n := range node {
		if i == 0 {
			buf.Printf("%v", n)
		} else {
			buf.Printf(", %v", n)
		}
	}
}

type ArrayElement interface {
	iArrayElement()
	SQLNode
}

func (*SQLVal) iArrayElement()   {}
func (*CastExpr) iArrayElement() {}
